package com.lfy.modules.common.aop;

import com.lfy.modules.common.anno.NoNeedLog;
import com.lfy.modules.common.domain.entity.ErrorLog;
import com.lfy.modules.common.intercept.AuthHandlerInterceptor;
import com.lfy.modules.common.mapper.ErrorLogMapper;
import com.lfy.modules.common.domain.entity.UserEntity;
import com.lfy.modules.common.utils.JsonUtils;
import com.lfy.modules.common.utils.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 *  统一日志管理
 *
 * @author 吕飞扬
 * @date 2023/5/20 23:52
 */
@Aspect
@Component
@Slf4j
public class Log {
    /**
     * 当前环境
     */
    private  Environment env;

    @Resource
    private ErrorLogMapper errorLogMapper;
    @Resource(name = "mainThreadPool")
    private ThreadPoolExecutor mainThreadPool;
    private static final Integer LOG_LENGTH = 5000;

    public Log(Environment environment){
        this.env=environment;
    }

    /**
     * spring框架切入点
     */
    @Pointcut("@within(org.springframework.stereotype.Repository)"+
//            "|| @within(org.springframework.stereotype.Service)"+
            "|| @within(org.springframework.web.bind.annotation.RestController)")
    public void springBeanPointcut(){

    }

    /**
     * 项目切入点
     * @param
     * @return
     */
    @Pointcut("within(com.lfy..*)")
    public void applicationPackagePointcut(){

    }

    @Around("springBeanPointcut() ")
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable{
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        NoNeedLog annotation = method.getAnnotation(NoNeedLog.class);
        if(Objects.nonNull(annotation)){
            return joinPoint.proceed();
        }
        Object proceed = null;

        try {

            if(log.isInfoEnabled()) {
                log.info("进入:{}.{}(),入参[k]:{},入参[v]:{},", joinPoint.getSignature().getDeclaringType(), joinPoint.getSignature().getName(),((CodeSignature)joinPoint.getSignature()).getParameterNames(), joinPoint.getArgs());
            }
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            proceed = joinPoint.proceed();
            stopWatch.stop();
            if (log.isInfoEnabled()) {
                log.info("退出:{}.{}(),结果:{},耗时:{}ms", joinPoint.getSignature().getDeclaringType(), joinPoint.getSignature().getName(), proceed, stopWatch.getLastTaskTimeMillis());
            }
        } catch (Throwable e) {
            saveErrorLog(joinPoint,e);
            throw e;
        }

        return proceed;
    }
    private void saveErrorLog(ProceedingJoinPoint joinPoint,Throwable e){
        RequestAttributes requestAttributes = RequestUtil.getRequestAttributes();
        UserEntity userEntity = AuthHandlerInterceptor.authLocal.get();
        mainThreadPool.execute(() -> {
            if(!Objects.isNull(requestAttributes)){
                RequestContextHolder.setRequestAttributes(requestAttributes);
            }
            ErrorLog errorLog = new ErrorLog();
            errorLog.setClassName(joinPoint.getSignature().getDeclaringType().getName());
            errorLog.setMethod(joinPoint.getSignature().getName());
            Object[] args = joinPoint.getArgs();
            String argStr = Stream.of(args).map(JsonUtils::toStr).collect(Collectors.joining(","));
            errorLog.setParams(argStr);
            String s = ExceptionUtils.getStackTrace(e);
            if(s.length() > LOG_LENGTH) {
                s = s.substring(0, LOG_LENGTH);
            }
            errorLog.setErrorMsg(s);

            errorLog.setUserDetail(JsonUtils.toStr(userEntity));
            if(env.acceptsProfiles(Profiles.of("dev", "test"))){
                String ip = RequestUtil.getIp();
                errorLog.setIpAddress(ip );
                errorLog.setPlace(RequestUtil.getPlace(ip));
             }else{
                String nginxRealIp = RequestUtil.getNginxRealIp();
                errorLog.setPlace(RequestUtil.getPlace(nginxRealIp));
                errorLog.setIpAddress(nginxRealIp);
            }

            errorLogMapper.insert(errorLog);
        });
    }
    @AfterThrowing(pointcut = "springBeanPointcut() && applicationPackagePointcut()",throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint,Throwable e){

        //判断运行环境
        if( env.acceptsProfiles(Profiles.of("dev", "test"))){
            log.error("错误方法：{}.{}()，cause:{},exception:{},e:{}",joinPoint.getSignature().getDeclaringType(),joinPoint.getSignature().getName(),
                    e.getCause() != null ? e.getCause() : "Null",
                    e.getMessage()!= null ? e.getMessage() : "Null",e);
        }else {
            log.error("错误方法：{}.{}()，cause:{},exception:{},e:{}",joinPoint.getSignature().getDeclaringType(),joinPoint.getSignature().getName(),
                    e.getCause() != null ? e.getCause() : "Null",
                    e.getMessage()!= null ? e.getMessage() : "Null",e);
        }

    }
}
